﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using GE;
using GE.Font;

namespace GE.Objects
{
    public class UpgradeStatus
    {
        public long atkNuff = 0;
        public long defNuff = 0;
        public float heroExpAdded = 0.0f;
        public long addedCastleDistance = 0;

        private long price = 20;
        private long priceCastle = 100;

        public long getPrice(long selectedUpgrade)
        {
            if (selectedUpgrade == 3)
                return priceCastle;
            else
                return price;
        }

        public void Upgrade(long selectedUpgrade)
        {
            switch (selectedUpgrade)
            {
                case 0:
                    price *= 5;
                    heroExpAdded += 0.1f;
                    break;
                case 1:
                    price *= 5;
                    atkNuff += 1;
                    break;
                case 2:
                    price *= 5;
                    defNuff += 1;
                    break;
                case 3:
                    priceCastle *= 5;
                    Config.mapWidth += 50;
                    break;
            }
        }
    }

    public class CharacterData
    {
        public String name, comment;
        public int level;
        public long atk, def, speed, hitpoint, exp;
        public int size;
        public long price;

        public CharacterData clone()
        {
            CharacterData data = new CharacterData();
            data.name = name;
            data.comment = comment;
            data.level = level;
            data.atk = atk;
            data.def = def;
            data.speed = speed;
            data.hitpoint = hitpoint;
            data.exp = exp;
            data.size = size;
            data.price = price;

            return data;
        }
    }

    public class Character
    {
        public enum AnimationState
        {
            FORWARD,
            BACKWARD,
            FORWARD_KNOCKBACK,
            BACKWARD_KNOCKBACK,
            STOP
        }

        protected String name, comment;
        public String Name { get { return name; } }
        public String Comment { get { return comment; } }

        protected int level;
        public int Level { get { return level; } set { this.level = value; } }

        protected Vector2 pos;
        public Vector2 Pos { get { return pos; } }

        AnimationState state = AnimationState.STOP;
        public AnimationState State
        { get { return this.state; } set { this.state = value; } }

        protected long atk;
        public virtual long getAtk() { return atk; }
        protected long def;
        public virtual long getDef() { return def; }

        protected float speed;
        public float Speed { get { return speed; } }
        protected float hitpoint;
        public float Hitpoint { get { return hitpoint; } set { this.hitpoint = value; } }
        protected long maxhitpoint;
        public long Maxhitpoint { get { return maxhitpoint; } }
        protected long exp;
        public long Exp { get { return exp; } }
        protected int size;
        protected long price;
        public long Price { get { return price; } }

        protected Character() { }

        public Character(CharacterData data, Vector2 pos)
        {
            this.name = data.name;
            this.comment = data.comment;

            this.level = data.level;
            this.pos = pos;

            this.hitpoint = data.hitpoint;
            this.maxhitpoint = data.hitpoint;

            this.atk = data.atk;
            this.def = data.def;
            this.speed = data.speed;

            this.exp = data.exp;
            this.size = data.size;
            this.price = data.price;
        }

        protected virtual void LevelUp() { this.level++; }

        private readonly long knockbackTime = 500;
        long knockbackCount = 500;
        float damage = 0;

        private float getHeight()
        {
            float pos = Math.Abs(knockbackCount - knockbackTime/2);
            return Config.Baseline + (pos * pos / 2500) - (250 * 250 / 2500);
        }

        public virtual void Update(GameTime gameTime)
        {
            float delta = speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
            switch (state)
            {
                case AnimationState.STOP:
                    break;
                case AnimationState.FORWARD:
                    pos += new Vector2(delta, 0);
                    break;
                case AnimationState.BACKWARD:
                    pos += new Vector2(-delta, 0);
                    break;
                case AnimationState.FORWARD_KNOCKBACK:
                    pos = new Vector2(pos.X + delta, getHeight());
                    knockbackCount -= gameTime.ElapsedGameTime.Milliseconds;
                    if (knockbackCount < 0)
                    {
                        knockbackCount = knockbackTime;
                        state = AnimationState.BACKWARD;
                    }
                    break;
                case AnimationState.BACKWARD_KNOCKBACK:
                    pos = new Vector2(pos.X - delta, getHeight());
                    knockbackCount -= gameTime.ElapsedGameTime.Milliseconds;
                    if (knockbackCount < 0)
                    {
                        knockbackCount = knockbackTime;
                        state = AnimationState.FORWARD;
                    }
                    break;
                default:
                    break;
            }
        }
        
        private long textWidth = 0;
        public long TextWidth { get { return textWidth; } }
        //assume begin
        public virtual void Draw(SpriteBatch spriteBatch, Vector2 scroll)
        {
            //draw name
            {
                Vector2 offset = pos - new Vector2(0, 10);
                foreach (char ch in name)
                {
                    FreetypeChar fc = FreetypeFont.getFont(size).getTexture(ch, hitpoint / (float)maxhitpoint, new Color(70, 70, 70), Color.Red);
                    spriteBatch.Draw(fc.tex,
                        offset + new Vector2(fc.bitmap.bitmap_left, -fc.bitmap.bitmap_top) + scroll, 
                        Color.White);
                    offset.X += fc.bitmap.advance_x >> 6;
                    offset.Y += fc.bitmap.advance_y >> 6;
                }
                textWidth = (long)(offset.X - pos.X)-10;
            }

            FreetypeFont.DrawString(spriteBatch, comment,
                pos - new Vector2(0, (size + 8) * 96.0f / 72.0f) + scroll,
                8,
                Color.White);

            FreetypeFont.DrawString(spriteBatch, "lv. " + level,
                pos - new Vector2(0, (size + 16) * 96.0f / 72.0f) + scroll,
                8,
                Color.White);


            switch (state)
            {
                case AnimationState.FORWARD_KNOCKBACK:
                    FreetypeFont.DrawString(spriteBatch, "-" + damage,
                        pos + new Vector2(30, -40) + scroll,
                        8,
                        Color.White);
                    break;
                case AnimationState.BACKWARD_KNOCKBACK:
                    FreetypeFont.DrawString(spriteBatch, "-" + damage,
                        pos + new Vector2(30, -40) + scroll,
                        8,
                        Color.White);
                    break;
            }
        }

        private static float calculateDamage(Character catk, Character cdef)
        {
            return (long)(Math.Max(
                (catk.getAtk() * Math.Pow(1.05f, catk.getAtk()) - cdef.getDef()) / Math.Pow(1.05f, cdef.getDef()),
                0.04 * catk.getAtk()) * 10);
        }
        public static void Battle(Character monster, Hero hero, UpgradeStatus status)
        {
            monster.atk -= status.atkNuff;
            monster.def -= status.defNuff;
            float monsterDamage = calculateDamage(hero, monster);
            float heroDamage = calculateDamage(monster, hero);
            monster.atk += status.atkNuff;
            monster.def += status.defNuff;

            monster.damage = Math.Max(0, monsterDamage);
            hero.damage = Math.Max(0, heroDamage);
            monster.hitpoint -= Math.Min(monster.hitpoint, monster.damage);
            hero.hitpoint -= Math.Min(hero.hitpoint, hero.damage);

            hero.pos += new Vector2(5, 0);
            monster.state = AnimationState.BACKWARD_KNOCKBACK;

            //level up
            if (monster.hitpoint <= 0)
            {
                if (hero.state != AnimationState.FORWARD_KNOCKBACK)
                    hero.state = AnimationState.BACKWARD;

                hero.exp += (long)(monster.exp * (1+status.heroExpAdded));
                hero.checkLevelUp();
            }
            else
            {
                hero.state = AnimationState.FORWARD_KNOCKBACK;
            }
        }
    }
}
